package com.hrbxlh.support.web.auth.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.hrbxlh.enums.RoleEnum;
import com.hrbxlh.exception.GlobalException;
import com.hrbxlh.pojo.SysUser;
import com.hrbxlh.support.web.auth.dao.ext.UserMapperExt;
import com.hrbxlh.support.web.auth.enums.PageEnum;
import com.hrbxlh.support.web.auth.po.LoginPO;
import com.hrbxlh.support.web.auth.service.AuthService;
import com.hrbxlh.support.web.auth.util.PropertiesUtil;
import com.hrbxlh.support.web.auth.vo.Ticket;
import com.hrbxlh.support.web.auth.vo.Token;
import com.hrbxlh.support.web.auth.vo.WXSign;
import com.hrbxlh.support.web.auth.vo.WeixinOpenidResponse;
import com.hrbxlh.util.ResultEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpSession;
import java.util.Calendar;
import java.util.Objects;
import java.util.UUID;

import static com.hrbxlh.support.web.auth.constant.ServiceConstant.*;

/**
 * @author cheer
 */
@Service
@Slf4j
public class AuthServiceImpl implements AuthService, ApplicationListener<ContextRefreshedEvent> {

    private static String token = "";

    private static String ticket = "";

    @Autowired
    private ThreadPoolTaskScheduler taskScheduler;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private UserMapperExt userMapperExt;

    private static String appid = PropertiesUtil.getProperty(
            "UtilFile.properties", "appid");

    private static String secret = PropertiesUtil.getProperty(
            "UtilFile.properties", "secret");

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        taskScheduler.scheduleWithFixedDelay(() -> {
            String token = getToken();
            if (StringUtils.isNotBlank(token)) {
                getTicket(token);
            }
        }, 3600000);
    }

    @Override
    public Integer authMobile(String code, HttpSession session) {
        String openid = getOpenidByCode(code);
        if (StringUtils.isEmpty(openid)) {
            throw new GlobalException("获取认证失败");
        }
        SysUser user = getUserByOpenid(openid);
        session.setAttribute(OPENID, openid);
        if (user == null) {
            return PageEnum.LOGIN_MOBILE.getCode();
        } else {
            session.setAttribute(IS_STUDENT, Objects.equals(RoleEnum.STUDENT.getCode(), user.getRoleId()));
            session.setAttribute(USER_INFO, user);
            return PageEnum.ATTENDANCE_MOBILE.getCode();
        }
    }

    @Override
    @Transactional
    public Integer login(LoginPO loginPO, HttpSession session) {

        String openid = getOpenidBySession(session);

        // 获取用户是否已绑定，绑定则跳转到相应页面
        SysUser info = getUserByOpenid(openid);
        if (info != null) {
            session.setAttribute(IS_STUDENT, Objects.equals(RoleEnum.STUDENT.getCode(), info.getRoleId()));
            session.setAttribute(USER_INFO, info);
            return PageEnum.ATTENDANCE_MOBILE.getCode();
        }

        // 根据用户账号、密码绑定验证
        info = getUserByStuorteaId(loginPO.getUserNumber());
        if (info == null) {
            throw new GlobalException(ResultEnum.ACCOUNT_NOT_EXIST.getMessage());
        }
        if (!Objects.equals(RoleEnum.STUDENT.getCode(), info.getRoleId())) {
            throw new GlobalException("系统只支持学生进行登陆");
        }
        if (!Objects.equals(DigestUtils.md5Hex(loginPO.getUserNumber() + loginPO.getPassword()),
                info.getPassword())) {
            throw new GlobalException(ResultEnum.USER_INFO_MATCH_FAILED.getMessage());
        }

        // 验证成功，将openid插入数据库
        updateUserOpenId(info.getUserNum(), openid);

        session.setAttribute(IS_STUDENT, Objects.equals(RoleEnum.STUDENT.getCode(), info.getRoleId()));
        session.setAttribute(USER_INFO, info);
        return PageEnum.ATTENDANCE_MOBILE.getCode();

    }

    @Override
    public String getOpenidBySession(HttpSession session) {
        if (session.getAttribute(OPENID) == null) {
            throw new GlobalException(ResultEnum.SESSION_TIMEOUT);
        }
        return (String) session.getAttribute(OPENID);
    }

    @Override
    public WXSign getWXSign(String url) {
        String nonceStr = getNonceStr();
        String timeStamp = String.valueOf(getTimeStamp());

        // sha1签名加密
        String signature = String.format("jsapi_ticket=%s&noncestr=%s&timestamp=%s&url=%s",
                ticket, nonceStr, timeStamp, url);
        signature = DigestUtils.shaHex(signature);

        return WXSign.builder().appid(appid).nonceStr(nonceStr).timestamp(timeStamp).signature(signature).build();
    }

    @Override
    public String getToken() {
        String url = String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential" +
                "&appid=%s&secret=%s", appid, secret);
        try {
            ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);
            if (responseEntity.getStatusCodeValue() == 200 && responseEntity.getBody() != null) {
                Token response = new ObjectMapper().readValue(
                        responseEntity.getBody(), Token.class);
                token = response.getAccess_token();
                return token;
            } else {
                log.error("get token failed, code: {}", responseEntity.getStatusCodeValue());
                throw new GlobalException(ResultEnum.SYSTEM_BUSY);
            }
        } catch (Exception e) {
            log.error("get token failed", e);
            throw new GlobalException(ResultEnum.SYSTEM_BUSY);
        }
    }

    @Override
    public String getTicket(String token) {
        String url = String.format("https://api.weixin.qq.com/cgi-bin/ticket/getticket" +
                "?access_token=%s&type=jsapi", token);
        try {
            ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);
            if (responseEntity.getStatusCodeValue() == 200 && responseEntity.getBody() != null) {
                Ticket response = new ObjectMapper().readValue(
                        responseEntity.getBody(), Ticket.class);
                ticket = response.getTicket();
                return ticket;
            } else {
                log.error("get ticket failed, code: {}", responseEntity.getStatusCodeValue());
                throw new GlobalException(ResultEnum.SYSTEM_BUSY);
            }
        } catch (Exception e) {
            log.error("get ticket failed", e);
            throw new GlobalException(ResultEnum.SYSTEM_BUSY);
        }
    }

    private String getNonceStr() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    private long getTimeStamp() {
        return (Calendar.getInstance().getTimeInMillis() / 1000);
    }

    private String getOpenidByCode(String code) {
        String url = String.format("https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&" +
                "code=%s&grant_type=authorization_code", appid, secret, code);
        try {
            ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);
            if (responseEntity.getStatusCodeValue() == 200 && responseEntity.getBody() != null) {
                WeixinOpenidResponse response = new ObjectMapper().readValue(
                        responseEntity.getBody(), WeixinOpenidResponse.class);
                return response.getOpenid();
            } else {
                log.error("get openid failed, code: {}", responseEntity.getStatusCodeValue());
                throw new GlobalException(ResultEnum.LOGIN_FAILED);
            }
        } catch (Exception e) {
            log.error("get openid failed", e);
            throw new GlobalException(ResultEnum.LOGIN_FAILED);
        }
    }

    private SysUser getUserByOpenid(String openid) {
        return userMapperExt.getUser(openid, null);
    }

    private SysUser getUserByStuorteaId(String stuorteaId) {
        return userMapperExt.getUser(null, stuorteaId);
    }

    private void updateUserOpenId(Integer id, String openid) {
        userMapperExt.updateOpenid(openid, id);
    }

}
